﻿#include "StringConv.h"

namespace sky
{

    template<class _CharType>
    inline void strconv_allocMemory(_CharType ** ppBuff, int length, _CharType * pszFixedBuffer, int fixedBufferLength)
    {
        assert(ppBuff != NULL);
        assert(length >= 0);
        if (pszFixedBuffer == NULL)
        {
            _CharType * pReallocBuff = static_cast<_CharType*>(_recalloc(*ppBuff, length, sizeof(_CharType)));
            *ppBuff = pReallocBuff;
            return;
        }

        if (*ppBuff != pszFixedBuffer)
        {
            if (length > fixedBufferLength)
            {
                _CharType * pReallocBuff = static_cast<_CharType*>(_recalloc(*ppBuff, length, sizeof(_CharType)));
                *ppBuff = pReallocBuff;
            }
            else
            {
                free(*ppBuff);
                *ppBuff = pszFixedBuffer;
            }
        }
        else
        {
            if (length > fixedBufferLength)
            {
                *ppBuff = static_cast<_CharType*>(calloc(length, sizeof(_CharType)));
            }
            else
            {
                *ppBuff = pszFixedBuffer;
            }
        }
    }

    template<class _CharType>
    inline void strconv_freeMemory(_CharType * pBuff, _CharType * pszFixedBuffer, int)
    {
        if (pBuff != pszFixedBuffer)
        {
            free(pBuff);
        }
    }

    strconv_a2w::strconv_a2w(const char *sz, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(sz, codePage);
    }

    strconv_a2w::strconv_a2w(char *sz, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(sz, codePage);
    }

    strconv_a2w::strconv_a2w(const std::string &str, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(str.c_str(), codePage);
    }

    strconv_a2w::strconv_a2w(std::string &str, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(str.c_str(), codePage);
    }

    strconv_a2w::~strconv_a2w()
    {
        strconv_freeMemory(m_psz, m_szBuffer, FIXED_CONV_BUF_LEN);
        m_szBuffer[0] = 0;
    }

    void strconv_a2w::conv(const char *sz, unsigned int codePage)
    {
        if (!sz)
        {
            m_psz = 0;
            return;
        }

        if (codePage == (unsigned int)-1)
        {
            codePage = ::GetACP();
        }

        int lengthA = ::lstrlenA(sz) + 1;
        int lengthW = lengthA;

        strconv_allocMemory(&m_psz, lengthW, m_szBuffer, FIXED_CONV_BUF_LEN);

        if (0 == ::MultiByteToWideChar(codePage, 0, sz, lengthA, m_psz, lengthW))
        {   
            //缓冲区不足
            if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                lengthW = ::MultiByteToWideChar(codePage, 0, sz, lengthA, NULL, 0);
                strconv_allocMemory(&m_psz, lengthW, m_szBuffer, FIXED_CONV_BUF_LEN);
                ::MultiByteToWideChar(codePage, 0, sz, lengthA, m_psz, lengthW);
            }
        }
    }

    strconv_w2a::strconv_w2a(const wchar_t *sz, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(sz, codePage);
    }

    strconv_w2a::strconv_w2a(wchar_t *sz, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(sz, codePage);
    }

    strconv_w2a::strconv_w2a(const std::wstring &str, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(str.c_str(), codePage);
    }

    strconv_w2a::strconv_w2a(std::wstring &str, unsigned int codePage) : 
        m_psz(m_szBuffer)
    {
        conv(str.c_str(), codePage);
    }

    strconv_w2a::~strconv_w2a()
    {
        strconv_freeMemory(m_psz, m_szBuffer, FIXED_CONV_BUF_LEN);
        m_szBuffer[0] = 0;
    }

    void strconv_w2a::conv(const wchar_t *sz, unsigned int codePage)
    {
        if (!sz)
        {
            m_psz = 0;
            return;
        }

        if (codePage == (unsigned int)-1)
        {
            codePage = ::GetACP();
        }

        int lengthW = ::lstrlenW(sz) + 1;
        int lengthA = lengthW * 4;

        strconv_allocMemory(&m_psz, lengthA, m_szBuffer, FIXED_CONV_BUF_LEN*4);

        if (0 == ::WideCharToMultiByte(codePage, 0, sz, lengthW, m_psz, lengthA, NULL, false))
        {
            // 缓冲区不足
            if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                lengthW = ::WideCharToMultiByte(codePage, 0, sz, lengthW, NULL, 0, NULL, FALSE);
                strconv_allocMemory(&m_psz, lengthW, m_szBuffer, FIXED_CONV_BUF_LEN * 4);
                ::WideCharToMultiByte(codePage, 0, sz, lengthW, m_psz, lengthA, NULL, false);
            }
        }
    }

    wchar_t * conv_a2w(const char * sz, unsigned int codePage)
    {
        wchar_t * wstr = 0;
        if (!sz)
        {
            return NULL;
        }

        if (codePage == (unsigned int)-1)
        {
            codePage = ::GetACP();
        }

        int lengthW = ::MultiByteToWideChar(codePage, 0, sz, -1, NULL, 0);

        wstr = new wchar_t[lengthW];

        int nRes = ::MultiByteToWideChar(codePage, 0, sz, -1, wstr, lengthW);
        if (nRes == 0)
        {
            return NULL;
        }

        return wstr;
    }

    char * conv_w2a(const wchar_t * sz, unsigned int codePage)
    {
        char * str = 0;
        if (!sz)
        {
            return NULL;
        }

        if (codePage == (unsigned int)-1)
        {
            codePage = ::GetACP();
        }

        int lengthA = ::WideCharToMultiByte(codePage, 0, sz, -1, NULL, 0, NULL, false);
        str = new char[lengthA];

        int nRes = ::WideCharToMultiByte(codePage, 0, sz, -1, str, lengthA, NULL, false);
        if (nRes == 0)
        {
            return NULL;
        }
        

        return str;
    }

    void conv_free(void* sz)
    {
        if (sz)
        {
            delete[] sz;
        }
    }


} // namespace sky